package com.hhd.flow.engine.lisener.impl;

import com.hhd.flow.engine.lisener.FlowListener;
import com.hhd.flow.engine.lisener.FlowListenerAnnotation;
import com.hhd.flow.engine.lisener.FlowProcessEventMultiCaster;
import com.hhd.flow.engine.lisener.event.*;
import com.hhd.flow.engine.utils.CommonUtils;
import com.hhd.flow.engine.vo.UserTask;
import com.hhd.flow.entity.FlowUserTask;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class DefaultFlowProcessEventMultiCaster implements FlowProcessEventMultiCaster {

    @Autowired(required = false)
    List<FlowListener> listeners;

    List<ParsedLisener> parsedLiseners;

    @Override
    public List<ParsedLisener> getParsedLiseners() {
        parseLiseners();
        return parsedLiseners;
    }

    @Override
    public void onStartProcessBefore(StartProcessBeforeEvent event) {
        getListeners(event.getStartProcessCommand().getFormTypeId(), event.getStartProcessCommand().getStartNode().getBeforeEnterListeners())
                .stream().forEach(flowListener -> flowListener.onStartProcessBefore(event));
    }

    @Override
    public void onStartProcessAfter(StartProcessAfterEvent event) {
        getListeners(event.getStartProcessCommand().getFormTypeId(), event.getStartProcessCommand().getStartNode().getAfterEnterListeners())
                .stream().forEach(flowListener -> flowListener.onStartProcessAfter(event));
    }

    @Override
    public void onBeforeEnterUserTask(BeforeEnterUserTaskEvent event) {
        getListeners(event.getContext().getFormTypeId(), event.getUserTask().getBeforeEnterListeners())
                .stream().forEach(l -> l.onBeforeEnterUserTask(event));
    }

    @Override
    public void onAfterEnterUserTask(AfterEnterUserTaskEvent event) {
        getListeners(event.getContext().getFormTypeId(), event.getUserTask().getAfterEnterListeners())
                .stream().forEach(l -> l.onAfterEnterUserTask(event));
    }

    @Override
    public void onUserTaskBeforeComplete(UserTaskCompleteEvent event) {
        UserTask userTask = event.getContext().getFlowModel().getUserTaskById(event.getFlowNode().getId());
        getListeners(event.getContext().getFormTypeId(), userTask.getBeforeComplete())
                .stream().forEach(l -> l.onUserTaskBeforeComplete(event));
    }

    @Override
    public void onUserTaskAfterComplete(UserTaskCompleteEvent event) {
        UserTask userTask = event.getContext().getFlowModel().getUserTaskById(event.getFlowNode().getId());
        getListeners(event.getContext().getFormTypeId(), userTask.getAfterComplete())
                .stream().forEach(l -> l.onUserTaskAfterComplete(event));
    }

    @Override
    public void onUserTaskCompleteBeforeEvery(UserTaskCompleteEveryEvent event) {
        FlowUserTask flowUserTask = event.getCompleteUserTaskCommand().getFlowUserTask();
        getListeners(flowUserTask.getFormTypeId(), flowUserTask.getEveryBeforeCompleteListener())
                .stream().forEach(l -> l.onUserTaskCompleteBeforeEvery(event));
    }

    @Override
    public void onUserTaskCompleteAfterEvery(UserTaskCompleteEveryEvent event) {
        FlowUserTask flowUserTask = event.getCompleteUserTaskCommand().getFlowUserTask();
        getListeners(flowUserTask.getFormTypeId(), flowUserTask.getEveryBeforeCompleteListener())
                .stream().forEach(l -> l.onUserTaskCompleteAfterEvery(event));
    }

    @Override
    public void onProcessBeforeApproved(ProcessApprovedEvent event) {
        getListeners(event.getContext().getFormTypeId(), null)
                .stream().forEach(l -> l.onProcessBeforeApproved(event));
    }

    @Override
    public void onProcessAfterApproved(ProcessApprovedEvent event) {
        getListeners(event.getContext().getFormTypeId(), null)
                .stream().forEach(l -> l.onProcessAfterApproved(event));
    }

    @Override
    public void onFlowUserTaskBeforeEnd(FlowUserTaskEndEvent event) {
        getListeners(event.getEndCommand().getFlowUserTask().getFormTypeId(), null)
                .stream().forEach(l -> l.onFlowUserTaskBeforeEnd(event));
    }

    @Override
    public void onFlowUserTaskAfterEnd(FlowUserTaskEndEvent event) {
        getListeners(event.getEndCommand().getFlowUserTask().getFormTypeId(), null)
                .stream().forEach(l -> l.onFlowUserTaskAfterEnd(event));
    }

    @Override
    public void onFlowUserTaskBeforeToForm(FlowUserTaskToFormEvent event) {
        getListeners(event.getToFormCommand().getProcessInstance().getFormTypeId(), null)
                .stream().forEach(l -> l.onFlowUserTaskBeforeToForm(event));
    }

    @Override
    public void onFlowUserTaskAfterToForm(FlowUserTaskToFormEvent event) {
        getListeners(event.getToFormCommand().getProcessInstance().getFormTypeId(), null)
                .stream().forEach(l -> l.onFlowUserTaskAfterToForm(event));
    }

    @Override
    public void onEnterNode(EnterNodeEvent event) {
        getListeners(event.getContext().getFormTypeId(), null)
                .stream().forEach(l -> l.onEnterNode(event));
    }

    @Override
    public void onPassNode(PassNodeEvent event) {
        getListeners(event.getContext().getFormTypeId(), null)
                .stream().forEach(l -> l.onPassNode(event));
    }

    @Override
    public void onProcessBeforeFinish(ProcessFinishEvent event) {
        getListeners(event.getFlowProcessInstance().getFormTypeId(), event.getFlowModel().getAfterListeners())
                .stream().forEach(l -> l.onProcessBeforeFinish(event));
    }

    @Override
    public void onProcessAfterFinish(ProcessFinishEvent event) {
        getListeners(event.getFlowProcessInstance().getFormTypeId(), event.getFlowModel().getAfterListeners())
                .stream().forEach(l -> l.onProcessAfterFinish(event));
    }

    @Override
    public void onCreateUserTaskEvent(CreateUserTaskEvent event) {
        getListeners(event.getFlowProcessInstance().getFormTypeId(), null)
                .stream().forEach(l -> l.onCreateUserTaskEvent(event));
    }

    @Override
    public void onBeforeWithdraw(WithdrawEvent event) {
        getListeners(event.getCommand().getFlowProcessInstance().getFormTypeId(), null)
                .stream().forEach(l -> l.onBeforeWidthdraw(event));
    }

    @Override
    public void onAfterWithdraw(WithdrawEvent event) {
        getListeners(event.getCommand().getFlowProcessInstance().getFormTypeId(), null)
                .stream().forEach(l -> l.onAfterWidthdraw(event));
    }

    public List<FlowListener> getListeners(String formType, String listenerNames) {
        if (CommonUtils.isEmpty(this.listeners)) return CommonUtils.newList();
        parseLiseners();

        return parsedLiseners.stream()
                .filter(listener -> listener.match(formType, listenerNames))
                .sorted(Comparator.comparing(l -> ((ParsedLisener) l).getOrder()).reversed())
                .map(ParsedLisener::getListener)
                .collect(Collectors.toList());
    }

    private void parseLiseners() {
        if (CommonUtils.isEmpty(parsedLiseners)) {
            synchronized (this) {
                if (CommonUtils.isEmpty(parsedLiseners)) {
                    doParseLiseners();
                }
            }
        }
    }

    private void doParseLiseners() {
        if (CommonUtils.isEmpty(this.listeners)) {
            this.parsedLiseners = CommonUtils.newList();
            return;
        }

        this.parsedLiseners = listeners.stream().map(this::parseListener).filter(Objects::nonNull).collect(Collectors.toList());
    }

    private ParsedLisener parseListener(FlowListener flowListener) {
        FlowListenerAnnotation annotation = CommonUtils.getAnnotation(flowListener, FlowListenerAnnotation.class);
        if (CommonUtils.isEmpty(annotation)) {
            return null;
        }
        ParsedLisener rse = new ParsedLisener();
        rse.setFormType(annotation.formType());
        rse.setPreset(annotation.preset());
        rse.setListenerName(annotation.listenerName());
        rse.setOrder(annotation.order());
        rse.setListener(flowListener);
        rse.setListenerTitle(annotation.listenerTitle());
        return rse;
    }

    @Getter
    @Setter
    public static class ParsedLisener {
        private String formType;
        private boolean preset;
        private String listenerName;
        private int order;
        String listenerTitle;
        FlowListener listener;

        public boolean match(String formType, String listenerNames) {
            if (preset) {
                return CommonUtils.isMatch(this.formType, formType); //如果是预制监听，那就只需要匹配formType就行了
            }
            if (CommonUtils.isEmpty(listenerNames)) {
                listenerNames = "";
            }
            return listenerNames.contains(this.listenerName); //如果不是预制监听器，那么只需要匹配监听器名称就行了
        }
    }

}
